/*
 * handle_pet.cpp
 *
 *  Created on: Aug 30, 2013
 *      Author: killerg
 */

#include "../../server_deps.h"
#include "../../server.h"
#include "../../client_session.h"
#include "../../server_console.h"

#include "../player_data.h"
#include "../game_cfg.h"
#include "../chat_mgr.h"

#include "share/net_s.h"
#include "share/util.h"
#include "share/cs_msg.pb.h"
#include "share/msg.h"
#include "share/clock.h"
#include "share/word_filter.h"

int32_t pick_one(int32_t setid, int32_t* num, int32_t* idx) {
    const tagProtoRandSet* set_proto = MAProto(RandSet, setid);
    if (NULL == set_proto) {
        return -1;
    }

    int index = random(set_proto->prob, set_proto->item_num);
    if (index < 0 || index >= set_proto->item_num) {
    	*idx = -1;
    	*num = 0;
        return -1;
    }

    if (NULL != num)
        *num = set_proto->num[index];

    if (NULL != idx)
        *idx = index;

    return set_proto->protoid[index];
}



bool check_item_by_type(sp_player_t player, int32_t type, int num, int32_t* protoid) {
    int32_t item_protoid = player->get_bag_item_protoid_by_type(EItemType(type));
    if (item_protoid < 0)
        return false;

    if (!player->can_del_item(item_protoid, num))
        return false;

    if (NULL != protoid)
        *protoid = item_protoid;
    return true;
}

bool check_item_by_serial(sp_player_t player, int32_t serial, int32_t type, int num) {
    item_t* item = player->get_bag_item(serial);
    if (NULL == item)
        return false;

    if (NULL == item->proto || type != item->proto->type)
        return false;

    if (!player->can_del_item_by_serial(serial, num))
        return false;

    return true;
}


// 创建宠物
void handle_ccreatepet(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    int32_t protoid = msg->createpet().protoid();
    int i=0;
    for (; i<5; ++i) {
        if (protoid == g_game_cfg->CreatePet[i])
            break;
    }
    if (5 == i) {
        sess->send_notify("不可创建");
        return;
    }

    if (NULL != player->find_pet(0)) {
        sess->send_notify("已创建过");
        return;
    }

    pet_t* pet = new pet_t;
    if (!pet->init(protoid, player->n.valid_pet_serial++, 1)) {
        --player->n.valid_pet_serial;
        pet->destroy();
        safe_del(pet);
    } else {
        player->add_pet(pet, false);
        player->att(Pl4s_CombatC1, pet->serial);
    }
}

bool test_combat(cs::OCombat* ocombat);

// 战斗测试
void handle_ccombat(client_session_t* sess, cs::CMsg* msg) {
    UTIL_LOG(T, "combat");

    if (NULL == sess->player)
        return;

    CREATE_CS_SMSG(SCombat, combat);
    test_combat(combat->mutable_combat());
    sess->send_msg(___msg);
}

// 改变改名
void handle_cpetname(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    if (!g_wordfilter->checkName(msg->petname().new_name().c_str(), EFM_Naming, 1, 8)) {
        sess->send_notify("名字不合法");
        return;
    }

    int32_t item_protoid = -1;
    if (!check_item_by_type(player, EIT_PetRename, 1, &item_protoid)) {
        sess->send_notify("物品不够");
        return;
    }


    auto pet = player->find_pet(msg->petname().serial());
    if (NULL == pet) {
        sess->send_notify("没有这个宠物");
        return;
    }

    const string & new_name = msg->petname().new_name();
    pet->att(Pess_Name, new_name);

    player->del_item(item_protoid, 1);
}

// 改变喂食
void handle_cpetfeed(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    auto petfeed = msg->petfeed();
    int item_serial = petfeed.item_serial();
    if (!check_item_by_serial(player, item_serial, EIT_PetFood, 1)) {
        sess->send_notify("无效物品");
        return;
    }

    auto pet = player->find_pet(petfeed.serial());
    if (NULL == pet) {
        sess->send_notify("没有这个宠物");
        return;
    }

    auto item = player->get_bag_item(item_serial);
    pet->add_exp(item->proto->misc1);

    player->del_item_by_serial(item_serial, 1);
    player->att(Pl4s_CntPetFeed, player->att(Pl4s_CntPetFeed) + 1);
}

// 改变性格
void handle_cpetcharacter(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == sess->player)
        return;

    int32_t item_protoid = -1;
    if (!check_item_by_type(player, EIT_PetCharacter, 1, &item_protoid)) {
        sess->send_notify("物品不够");
        return;
    }

    auto pet = player->find_pet(msg->petcharacter().serial());
    if (NULL == pet) {
        sess->send_notify("没有这个宠物");
        return;
    }

    int32_t character = pet->att(Pe4s_Character);

    int32_t index = random(0, g_protomgr->rand_characters.size()-1);
    int32_t new_char = g_protomgr->rand_characters[index];
    if (new_char == character)
        return;

    pet->att(Pe4s_Character, new_char);
    pet->calc_att();

    player->del_item(item_protoid, 1);
    player->att(Pl4s_WeeklyCnt8WashChar, player->att(Pl4s_WeeklyCnt8WashChar) + 1);
}

// 放生
void handle_cpetrelease(client_session_t* sess, cs::CMsg* msg) {
    if (NULL == sess->player)
        return;

    auto player = sess->player;
    auto to_remove = player->find_pet(msg->petrelease().serial());
    if (NULL == to_remove) {
        sess->send_notify("宠物不存再");
        return;
    }

	for (int i=0; i<MaxTrainRoomNum; ++i) {
		if (to_remove->serial == player->_train[i].petid) {
			sess->send_notify("training");
			return;
		}
	}

    int pet_rewardid = to_remove->proto->release_reward;

    if (!EQuality_IsValid(to_remove->proto->quality)) {
    	return;
    }
    const tagProtoPetLevel* petlevel_proto = ARProto(PetLevel, to_remove->att(Pe4s_Level));
    if (NULL == petlevel_proto)
        return;

    int hearts = 0;
	for (int i = 0; i < PetAptitudeNum; ++i) {
		int aptitudes = to_remove->att(EPeAtt4Save(Pe4s_AptitudeBegin + i)) - to_remove->proto->aptitude[i];
		for (int i=0; i<aptitudes; ++i) {
	        const tagProtoPetRearConsume* rear_consume_proto = ARProto(PetRearConsume, i);
	        if (NULL == rear_consume_proto) {
	            break;
	        }
	        const tagProtoConsume* consume_proto = MAProto(Consume, rear_consume_proto->game_money_consume);
	        if (NULL == consume_proto) {
	        	break;
	        }
	        for (int ii=0; ii<5; ++ii) {
	        	if (consume_proto->item_proto[ii] == g_game_cfg->HeartProtoid) {
	        		hearts += consume_proto->item_num[ii];
	        		break;
	        	}
	        }
		}
	}

	player->add_item(g_game_cfg->HeartProtoid, hearts * (g_game_cfg->ReleaseHeartRate / 100.0f));

    player->reward(petlevel_proto->release_reward[to_remove->proto->quality]);
    player->reward(pet_rewardid);

    player->remove_pet(to_remove->serial);
    to_remove->destroy();
    safe_del(to_remove);

    player->att(Pl4s_CntReleasePet, player->att(Pl4s_CntReleasePet) + 1);
}

void handle_cpetcombatpos(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    int32_t pet_serial = msg->petcombatpos().serial();
    int32_t to_pos = msg->petcombatpos().pos();

    if (to_pos >= PlayerCombatCSlotNum && to_pos < PlayerCombatSlotNum) {
    	int watch_num = player->att(Pl4s_WatchNum);
    	if (to_pos >= watch_num + PlayerCombatCSlotNum) {
            sess->send_notify("观战栏未开启");
            return;
    	}
    }

    // 检查宠物
    pet_t* pet = player->find_pet(pet_serial);
    if (NULL == pet) {
        sess->send_notify("无效宠物");
        return;
    }

    int32_t pet_pos = -1;
    int32_t poses[PlayerCombatSlotNum] = {-1};
    for (int i=0; i<PlayerCombatSlotNum; ++i) {
        int32_t cur_pet_serial = player->att(EPlAtt4Save(i + Pl4s_CombatBegin));
        poses[i] = cur_pet_serial;
        if (cur_pet_serial == pet_serial)
            pet_pos = i;
    }

    // 当前在栏上，并且目标位置空
    if (pet_pos >= 0 && to_pos < 0) {
        poses[pet_pos] = -1;
    // 当前在栏上，并且目标位置不空
    } else if (pet_pos >= 0 && to_pos >= 0) {
        poses[pet_pos] = poses[to_pos];
        poses[to_pos] = pet_serial;
    // 当前不在栏上，并且目标位置空
    } else if (pet_pos < 0 && to_pos < 0) {

    // 当前不在栏上，并且目标位置不空
    } else if (pet_pos < 0 && to_pos >= 0) {
        poses[to_pos] = pet_serial;
    }

    // 设置
    for (int i=0; i<PlayerCombatSlotNum; ++i)
        player->att(EPlAtt4Save(i + Pl4s_CombatBegin), poses[i]);

    if ((pet_pos >= 0 && pet_pos < PlayerCombatCSlotNum) ||
        (to_pos >= 0 && to_pos < PlayerCombatCSlotNum)) {
    	player->image_stamp = g_clock->tt_now;
    }
}

// 宠物培养
void handle_cpetrear(client_session_t* sess, cs::CMsg* msg) {
    auto rear = msg->petrear();

    auto player = sess->player;
    if (NULL == player)
        return;

    auto pet = player->find_pet(rear.serial());
    if (NULL == pet) {
        sess->send_notify("没有这个宠物");
        return;
    }

    if (rear.reset()) {
    	pet->att(Pe4s_UsedPotentPoint, 0);
        for (int i=0; i<PetAptitudeNum; ++i)
            pet->att(EPeAtt4Save(i + Pe4s_AptitudeBegin), pet->proto->aptitude[i]);
    } else {
        int32_t apt_index = rear.lock_pos();
        if (apt_index < 0 || apt_index >= PetAptitudeNum) {
            sess->send_notify("资质枚举错误");
            return;
        }

        int32_t cur_aptitude = pet->att(EPeAtt4Save(apt_index + Pe4s_AptitudeBegin));
        int32_t def_aptitude = pet->proto->aptitude[apt_index];
        int32_t ex_num = cur_aptitude - def_aptitude;
        int32_t potent_point = pet->att(Pe4s_PotentPoint);
        int32_t used_potent_point = pet->att(Pe4s_UsedPotentPoint);

        if (ex_num >= potent_point * .6) {
            sess->send_notify("已超过单项最大资质");
            return;
        }

        if (used_potent_point >= potent_point) {
            sess->send_notify("潜能点不足");
            return;
        }

        const tagProtoPetRearConsume* rear_consume_proto = ARProto(PetRearConsume, ex_num);
        if (NULL == rear_consume_proto) {
            sess->send_notify("pet_rear_consume.txt 配置表中没有 #d", ex_num);
            return;
        }

        int32_t consumeid = rear.use_money() ?
                rear_consume_proto->money_consume :
                rear_consume_proto->game_money_consume;

        if (!player->can_consume(consumeid)) {
            sess->send_notify("消耗不满足");
            return;
        }

        ++used_potent_point;

        if (rear.has_use_money() && rear.use_money()) {
            ++cur_aptitude;
            if (prob(g_game_cfg->PetRearExplodeProb))
                cur_aptitude += 2;
        } else if (prob(g_game_cfg->PetRearGameMoneyProb)) {
            ++cur_aptitude;
        }

        player->consume(consumeid);
        player->att(Pl4s_AliveCnt4Rear, player->att(Pl4s_AliveCnt4Rear) + 1);
        player->att(Pl4s_WeeklyCnt2Rear, player->att(Pl4s_WeeklyCnt2Rear) + 1);
        pet->att(Pe4s_UsedPotentPoint, used_potent_point);
        pet->att(EPeAtt4Save(apt_index + Pe4s_AptitudeBegin), cur_aptitude);

        pet->calc_att();
        player->att(Pl4s_CntEnhancePet, player->att(Pl4s_CntEnhancePet) + 1);
    }
}
void handle_cpetevo(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    auto petevo = msg->petevo();

    auto pet = player->find_pet(petevo.serial());
    if (NULL == pet) {
        sess->send_notify("无此宠物");
        return;
    }

    int32_t level = pet->att(Pe4s_Level);
    if (level < pet->proto->evolve_level) {
        sess->send_notify("等级不够");
        return;
    }

    auto to_proto = MAProto(Pet, pet->proto->evolve_to);
    if (NULL == to_proto) {
        sess->send_notify("进化不了");
        return;
    }

    int32_t needitemid = pet->proto->evo_itemid;
    if (!player->can_del_item(needitemid, 1)) {
        sess->send_notify("进化证不够");
        return;
    }

    auto old_proto = pet->proto;
    pet->att(Pe4s_Proto, to_proto->id);

    // 变异
    int32_t extrafamaly = pet->att(Pe4s_ExtraFamily);
    if (extrafamaly < 0 && to_proto->mutate_family >= 0 && prob(to_proto->mutate_prob))
        pet->att(Pe4s_ExtraFamily, to_proto->mutate_family);

    // 无限进化，只给一次潜能点
    int32_t potent_point = pet->att(Pe4s_PotentPoint);
    if (old_proto->id != to_proto->id && to_proto->potent_point > 0)
        pet->att(Pe4s_PotentPoint, potent_point + to_proto->potent_point);

    // 魔法印记槽
    int32_t nskillslotnum = max<int32_t>(pet->att(Pe4s_NormalSkillSlotNum), to_proto->def_normalskill_num);
    pet->att(Pe4s_NormalSkillSlotNum, nskillslotnum);

    // 给技能
    auto empty_slot = (EPeAtt4Save)-1;
    for (int i=0; i<PetSpecSkillLearnMaxNum; ++i) {
        auto skill_index = EPeAtt4Save(Pe4s_SkillSSlotBegin + i);
        // 星级技能
        if (i == PetSpecSkillLearnMaxNum - 1)
        	continue;

        if (pet->att(skill_index) < 0) {
            empty_slot = skill_index;
            break;
        }
    }
    if (EPetAtt_IsValid(EPetAtt(empty_slot - Pe4s_SkillSSlotBegin)) && to_proto->skill_set >= 0) {
        auto skillset_proto = MAProto(SkillSet, to_proto->skill_set);
        if (NULL != skillset_proto) {
            int32_t rand_skill = random(skillset_proto->item);
            if (rand_skill >= 0) {
                pet->att(empty_slot, rand_skill);
                if (player->skillhas.count(rand_skill / 10) == 0) {
                    player->skillhas.insert(rand_skill / 10);
                    player->skillhas_added.push_back(rand_skill / 10);
                }
            }
        }
    }

    pet->proto = to_proto;

    // 重新计算属性影响
    pet->calc_att();

    player->del_item(needitemid, 1);

    if (needitemid == g_game_cfg->PetEvoRewardNeedItemId)
    	player->reward(g_game_cfg->PetEvoReward);

    if (to_proto->cardid >= 0 && player->pethas.count(to_proto->cardid) <= 0) {
        player->pethas.insert(to_proto->cardid);
        player->pethas_added.push_back(to_proto->cardid);
    }
}

void handle_cunlocknskillslot(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    // 宠物
    auto unlock = msg->unlocknskillslot();
    auto pet = player->find_pet(unlock.serial());
    if (NULL == pet) {
        sess->send_notify("无此宠物");
        return;
    }

    int nskillslotnum = pet->att(Pe4s_NormalSkillSlotNum);
    if (nskillslotnum + 1 > pet->proto->max_normalskill_num) {
        sess->send_notify("槽数错误");
        return;
    }

    int need_money = 0;
    if (nskillslotnum >=5 && nskillslotnum < 10) {
        need_money = g_game_cfg->NSkillUnlockMoney[nskillslotnum - 5];
    }

    int64_t money = player->att(Pl8s_Money);
    if (money < need_money) {
        sess->send_notify("消耗不够");
        return;
    }

    ++nskillslotnum;
    money -= need_money;

    player->att(Pl8s_Money, money);
    pet->att(Pe4s_NormalSkillSlotNum, nskillslotnum);
}

void handle_cpetskilllearn(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    // 宠物
    auto learn = msg->petskilllearn();
    auto pet = player->find_pet(learn.serial());
    if (NULL == pet) {
        sess->send_notify("无此宠物");
        return;
    }

    // 物品
    if (!check_item_by_serial(player, learn.item_serial(), EIT_PetSkillLearn, 1)) {
        sess->send_notify("无此物品");
        return;
    }
    auto item = player->get_bag_item(learn.item_serial());

    // 技能
    auto skill_proto = MAProto(Skill, item->proto->misc1);
    if (NULL == skill_proto) {
        sess->send_notify("无效技能");
        return;
    }

    if (skill_proto->family != pet->proto->family && skill_proto->family != pet->att(Pe4s_ExtraFamily)) {
        sess->send_notify("系别不对");
        return;
    }

    // 检查是否已经学习并且找空位
    EPeAtt4Save empty_slot = (EPeAtt4Save)-1;
    // 特技
    if (skill_proto->family >= 0) {
        for (int i=0; i<PetSpecSkillNum; ++i) {
        	// 星级技能
        	if (i == PetSpecSkillLearnMaxNum - 1)
        		continue;

            EPeAtt4Save slot = EPeAtt4Save(i + Pe4s_SkillSSlotBegin);
            int32_t skill = pet->att(slot);
            if (skill_proto->id / 10 == skill / 10) {
                sess->send_notify("已经有这个技能");
                return;
            }
            if (skill < 0 && !EPeAtt4Save_IsValid(empty_slot))
                empty_slot = slot;
        }
    // 普通技能
    } else {
        for (int i=0; i<PetNormalSkillNum; ++i) {
            EPeAtt4Save slot = EPeAtt4Save(i + Pe4s_SkillNSlotBegin);
            int32_t skill = pet->att(slot);
            if (skill_proto->id / 10 == skill / 10) {
                sess->send_notify("已经有这个技能");
                return;
            }
            if (skill < 0 && !EPeAtt4Save_IsValid(empty_slot))
                empty_slot = slot;
        }
    }

    if (!EPeAtt4Save_IsValid(empty_slot)) {
        sess->send_notify("没有空位");
        return;
    }

    if (skill_proto->family < 0) {
        int32_t empty_index = empty_slot - Pe4s_SkillNSlotBegin;
        if (empty_index >= pet->att(Pe4s_NormalSkillSlotNum)) {
            sess->send_notify("最大魔法印记数量限制");
            return;
        }
    } else {
        int32_t empty_index = empty_slot - Pe4s_SkillSSlotBegin;
        if (empty_index >= PetSpecSkillLearnMaxNum) {
            sess->send_notify("最大特技数量限制");
            return;
        }
        if (player->skillhas.count(skill_proto->id / 10) == 0) {
            player->skillhas.insert(skill_proto->id / 10);
            player->skillhas_added.push_back(skill_proto->id / 10);
        }
        player->att(Pl4s_CntPetLearnSkill, player->att(Pl4s_CntPetLearnSkill)+1);
    }

    pet->att(empty_slot, skill_proto->id);
    pet->calc_att();

    // 扣除
    player->del_item_by_serial(learn.item_serial(), 1);
}

void handle_cpetskillforget(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    auto forget = msg->petskillforget();
    auto pet = player->find_pet(forget.serial());
    if (NULL == pet) {
        sess->send_notify("无此宠物");
        return;
    }

    int32_t skill_index = forget.skill_index();
    if (skill_index < 0 || skill_index >= (PetSpecSkillNum + PetNormalSkillNum)) {
        sess->send_notify("索引异常");
        return;
    }

	// 星级技能
	if (skill_index == PetSpecSkillLearnMaxNum - 1) {
        sess->send_notify("不能遗忘");
        return;
    }

    auto slot = EPeAtt4Save(skill_index + Pe4s_SkillSSlotBegin);
    pet->calc_att();

    pet->att(slot, -1);
}

void handle_cpetskilllevelup(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    auto levelup = msg->petskilllevelup();
    auto pet = player->find_pet(levelup.serial());
    if (NULL == pet) {
        sess->send_notify("无此宠物");
        return;
    }

    // 物品
    if (!check_item_by_serial(player, levelup.item_serial(), EIT_PetSkillLevelUp, 1)) {
        sess->send_notify("无此物品");
        return;
    }
    auto item = player->get_bag_item(levelup.item_serial());

    // 技能槽
    int32_t skill_index = levelup.skill_index();
    if (skill_index < 0 || skill_index >= 18) {
        sess->send_notify("索引异常");
        return;
    }
	// 星级技能
	if (skill_index == PetSpecSkillLearnMaxNum - 1) {
        sess->send_notify("不能升级");
        return;
    }

    auto slot = EPeAtt4Save(skill_index + Pe4s_SkillSSlotBegin);

    // 技能等级
    int32_t skill = pet->att(slot);
    int32_t level = skill % 10;
    auto next_proto = MAProto(Skill, skill+1);
    if (NULL == next_proto) {
        sess->send_notify("已经最高级");
        return;
    }

    if (item->proto->misc1 != level) {
        sess->send_notify("等级不匹配");
        return;
    }

    // 升级
    pet->att(slot, next_proto->id);
    pet->calc_att();

    player->del_item_by_serial(levelup.item_serial(), 1);
}

void handle_ceggrandom(client_session_t* sess, cs::CMsg* msg) {
    auto eggrandom = msg->eggrandom();

    auto player = sess->player;
    if (NULL == player)
        return;

    auto type = eggrandom.type();
    if (!cs::CEggRandom_ConsumeType_IsValid(type)) {
    	sess->send_notify("无效消耗");
    	return;
    }

    int egglevel = eggrandom.egg_level();
    const tagProtoEggRandom* egglevel_proto = ARProto(EggRandom, egglevel);
    if (NULL == egglevel_proto)
        return;

    if (!player->can_consume(egglevel_proto->consume[type])) {
        sess->send_notify("消耗不足");
        return;
    }

    int32_t index = random(egglevel_proto->item_prob, PetEggLevelEggNum);
    if (index < 0) {
        sess->send_notify("没有蛋");
        return;
    }

    player->add_item(egglevel_proto->item_proto[index], 1);

    player->consume(egglevel_proto->consume[type]);

    player->att(Pl4s_AliveCnt7RandEgg, player->att(Pl4s_AliveCnt7RandEgg) + 1);
    sess->stat.add_consume_byid(ECT_RandEgg, egglevel_proto->id + 1, egglevel_proto->consume[type]);
}

void handle_chatcheggunlock(client_session_t* sess, cs::CMsg* msg) {
    auto player = sess->player;
    if (NULL == player)
        return;

    int32_t slotnum = player->att(Pl4s_HatchSlotNum);
    int32_t consumeid = -1;
    switch (slotnum) {
    case 2:
        consumeid = g_game_cfg->HatchEggUnlock3Consume;
        break;
    case 3:
        consumeid = g_game_cfg->HatchEggUnlock4Consume;
        break;
    default:
        sess->send_notify("无效的槽数");
        return;
        break;
    }

    if (!player->can_consume(consumeid)) {
        sess->send_notify("消耗不足");
        return;
    }

    player->att(Pl4s_HatchSlotNum, slotnum+1);
    player->consume(consumeid);
}

void handle_cegghatch(client_session_t* sess, cs::CMsg* msg) {
    auto egghatch = msg->egghatch();

    auto player = sess->player;
    if (NULL == player)
        return;

    if (player->pets.size() >= PlayerMaxPetNum) {
        sess->send_notify("达到最大宠物数量");
        return;
    }

    int32_t index = egghatch.index();
    if (index < 0 || index > EggHatchIndexNum) {
        sess->send_notify("索引超出范围");
        return;
    }

    int32_t hatchegg_num = player->att(Pl4s_HatchSlotNum);
    if (index >= hatchegg_num) {
        sess->send_notify("已锁");
        return;
    }

    EPlAtt4Save egg_slot = (EPlAtt4Save)(Pl4s_HatchEggidBegin + index);
    EPlAtt4Save time_slot = (EPlAtt4Save)(Pl4s_HatchFinTimeBegin + index);

    int32_t egg_protoid = player->att(egg_slot);
    int32_t hatch_fin = player->att(time_slot);

    if (egghatch.has_get_pet() && egghatch.get_pet()) {
    	if (player->pets.size() >= PlayerMaxPetNum) {
            sess->send_notify("宠物数量达到最大");
            return;
    	}

    	if (hatch_fin < 0) {
            sess->send_notify("时间无效");
            return;
        }

        if (egg_protoid < 0) {
            player->att(time_slot, -1);
            sess->send_notify("无效蛋");
            return;
        }

        if (g_clock->tt_now < hatch_fin) {
            sess->send_notify("时间未到");
            return;
        }

        const tagProtoItem* egg_proto = MAProto(Item, egg_protoid);
        if (NULL != egg_proto && egg_proto->misc1 >= 0) {
            pet_t* pet = new pet_t;
            if (!pet->init(egg_proto->misc1, player->n.valid_pet_serial++, 1)) {
                --player->n.valid_pet_serial;
                pet->destroy();
                safe_del(pet);
            } else {
                player->add_pet(pet, false);
                if (pet->proto->quality >= g_game_cfg->BroadHatchEggQuality)
                    g_chat->broad_scroll(fmt_cstr("0,%s,%d", player->att(Plss_Name).c_str(), pet->proto->id));
            }

            player->att(Pl4s_CntHatchEgg, player->att(Pl4s_CntHatchEgg) + 1);
            player->att(Pl4s_AliveCnt1Hatch, player->att(Pl4s_AliveCnt1Hatch) + 1);
            player->att(Pl4s_WeeklyCnt1HatchEgg, player->att(Pl4s_WeeklyCnt1HatchEgg) + 1);

            sess->stat.add_event(EET_HatchEgg);
        }

        egg_protoid = -1;
        hatch_fin = -1;
    } else if (egghatch.has_get_egg() && egghatch.get_egg()) {
    	if (egg_protoid >= 0) {
            const tagProtoItem* egg_proto = MAProto(Item, egg_protoid);
            if (NULL == egg_proto) {
                sess->send_notify("无效蛋");
                return;
            }

            player->add_item(egg_protoid, 1);
    	}

    	egg_protoid = -1;
    	hatch_fin = -1;
	} else {
        if (egg_protoid < 0) {
            if (!egghatch.has_item_serial()) {
                sess->send_notify("无蛋");
                return;
            }

            if (!check_item_by_serial(player, egghatch.item_serial(), EIT_PetEgg, 1)) {
                sess->send_notify("无蛋");
                return;
            }
            auto egg = player->get_bag_item(egghatch.item_serial());

            egg_protoid = egg->proto->id;
            if (egg->proto->misc2 > 0)
                hatch_fin = g_clock->tt_now + egg->proto->misc2 * 60 * 60;
            else
                hatch_fin = 0;

            player->del_item_by_serial(egghatch.item_serial(), 1);
        } else {
            const tagProtoItem* egg_proto = MAProto(Item, egg_protoid);
            if (NULL == egg_proto) {
                sess->send_notify("无效蛋");
                return;
            }

            if (egghatch.has_item_serial()) {
                sess->send_notify("duplicate msg");
                return;
            }

            if (!player->can_consume(egg_proto->misc3)) {
                sess->send_notify("消耗不足");
                return;
            }

            hatch_fin = 0;

            player->consume(egg_proto->misc3);
        }
    }
    player->att(egg_slot, egg_protoid);
    player->att(time_slot, hatch_fin);
}

void handle_cpetinherit(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int32_t frompetid = msg->petinherit().frompet();
	int32_t topetid = msg->petinherit().topet();

	auto frompet = player->find_pet(frompetid);
	auto topet = player->find_pet(topetid);

	if (!frompet) {
		sess->send_notify("no from pet");
		return;
	}
	if (!topet) {
		sess->send_notify("no to pet");
		return;
	}

	const auto* fs_proto = ARProto(PetFriendStage, frompet->att(Pe4s_FStage));
	if (!fs_proto) {
		sess->send_notify("invalid fsproto");
		return;
	}

	if (!player->can_consume(fs_proto->tran_consumeid)) {
		sess->send_notify("can not consume");
		return;
	}

	player->consume(fs_proto->tran_consumeid);

	topet->att(Pe4s_FriVal, frompet->att(Pe4s_FriVal));
	topet->att(Pe4s_FStage, frompet->att(Pe4s_FStage));
	topet->att(Pe4s_FSAtt1, frompet->att(Pe4s_FSAtt1));
	topet->att(Pe4s_FSAtt2, frompet->att(Pe4s_FSAtt2));
	topet->att(Pe4s_FSAtt3, frompet->att(Pe4s_FSAtt3));
	topet->att(Pe4s_FSAtt4, frompet->att(Pe4s_FSAtt4));
	topet->att(Pe4s_FSAtt5, frompet->att(Pe4s_FSAtt5));
	topet->att(Pe4s_FSAtt6, frompet->att(Pe4s_FSAtt6));
	topet->att(Pe4s_FSMod1, frompet->att(Pe4s_FSMod1));
	topet->att(Pe4s_FSMod2, frompet->att(Pe4s_FSMod2));
	topet->att(Pe4s_FSMod3, frompet->att(Pe4s_FSMod3));
	topet->att(Pe4s_FSMod4, frompet->att(Pe4s_FSMod4));
	topet->att(Pe4s_FSMod5, frompet->att(Pe4s_FSMod5));
	topet->att(Pe4s_FSMod6, frompet->att(Pe4s_FSMod6));

	frompet->att(Pe4s_FriVal, 0);
	frompet->att(Pe4s_FStage, 0);
	frompet->att(Pe4s_FSAtt1, -1);
	frompet->att(Pe4s_FSAtt2, -1);
	frompet->att(Pe4s_FSAtt3, -1);
	frompet->att(Pe4s_FSAtt4, -1);
	frompet->att(Pe4s_FSAtt5, -1);
	frompet->att(Pe4s_FSAtt6, -1);
	frompet->att(Pe4s_FSMod1, 0);
	frompet->att(Pe4s_FSMod2, 0);
	frompet->att(Pe4s_FSMod3, 0);
	frompet->att(Pe4s_FSMod4, 0);
	frompet->att(Pe4s_FSMod5, 0);
	frompet->att(Pe4s_FSMod6, 0);

	fs_proto = ARProto(PetFriendStage, frompet->att(Pe4s_FStage));

	int idx = rand() % fs_proto->g_randatts.size();
	frompet->att(EPeAtt4Save(Pe4s_FSAtt1 + fs_proto->id),
			fs_proto->g_randatts[idx].att);
	frompet->att(EPeAtt4Save(Pe4s_FSMod1 + fs_proto->id),
			random(fs_proto->g_randatts[idx].min,
					fs_proto->g_randatts[idx].max));

	frompet->calc_att();
	topet->calc_att();

}


void handle_cpetlvlupreward		(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	int index = player->att(Pl4s_PetLvlUpRewardIndex);
	if (index < 0 || index >= 6) {
		sess->send_notify("exceed index");
		return;
	}

	int needlevel = g_game_cfg->PetLvlUpRewardLvl[index];

	bool arrive = false;
	for (auto &pet : player->pets) {
		if (pet.second->att(Pe4s_Level) >= needlevel) {
			arrive = true;
			break;
		}
	}

	if (!arrive) {
		sess->send_notify("level not enough");
		return;
	}

	int32_t rewardid = g_game_cfg->PetLvlUpRewardId[index];
	if (rewardid >= 0)
		player->reward(rewardid);

	player->att(Pl4s_PetLvlUpRewardIndex, index+1);
}

void handle_cpetstarfeedegg		(client_session_t* sess, cs::CMsg* msg) {
	auto player = sess->player;
	if (!player)
		return;

	auto tofeed = player->find_pet(msg->petstarfeedegg().tofeed());
	if (!tofeed) {
		sess->send_notify("find not pet");
		return;
	}

	auto* item = player->get_bag_item(msg->petstarfeedegg().eggserial());
	if (!item) {
		sess->send_notify("find not egg");
		return;
	}

	const auto* eggproto = MAProto(Pet, item->proto->misc1);
	if (!eggproto) {
		sess->send_notify("invalid egg");
		return;
	}

	if (eggproto->quality < tofeed->proto->star_egg_require) {
		sess->send_notify("蛋品质不够");
		return;
	}

	player->del_item_by_serial(item->serial, 1);

	int32_t starexp = tofeed->att(Pe4s_StarExp);
	int32_t star = tofeed->att(Pe4s_Star);

	starexp += eggproto->star_exp;

	const auto* starproto = ARProto(PetStar, star);
	while (starproto && starexp >= starproto->expreq) {
		starexp -= starproto->expreq;
		++star;
		starproto = ARProto(PetStar, star);
	}

	int curband = 0;
	for (int i=0; i<3; ++i)
		if (star >= g_game_cfg->PetStarSkillBand[i])
			curband = i;

	int32_t newskill = tofeed->proto->star_skills[curband];
	tofeed->att(Pe4s_SkillSSlot5, newskill);
	tofeed->att(Pe4s_StarExp, starexp);
	tofeed->att(Pe4s_Star, star);
	tofeed->calc_att();

	if (newskill >= 0 && player->skillhas.count(newskill / 10) == 0) {
        player->skillhas.insert(newskill / 10);
        player->skillhas_added.push_back(newskill / 10);
	}
}
